package models;

import javax.persistence.*;

import play.db.jpa.Model;

@Entity
public class PanneauSolaire extends Model {
    
	static final int N = 8760;
	
	private Double _surfacePanneau;
	private Double _puissancePanneau;
	private Double _coefTemperature;
	
	private Double calculHauteur(Double latitude, Double dec, int ah) {
		// Hauteur
		// h = asind(sind(Lat) * sind(Dec) + cosd(Lat) * cosd(Dec) * cosd(Ah));
		Double h = Math.toDegrees(Math.asin(Math.sin(Math.toRadians(latitude)) * Math.sin(Math.toRadians(dec)) + Math.cos(Math.toRadians(latitude)) * Math.cos(Math.toRadians(dec)) * Math.cos(Math.toRadians(ah))));
		
		if (h < 0) {
			h = 0.0;
		}

		return h;
	}
	
	private Double calculAzimuth(Double dec, int ah, Double h) {
		// Azimuth
		// a = asind((cosd(Dec) * sind(Ah)) / cosd(h));
		return Math.toDegrees(Math.asin((Math.cos(Math.toRadians(dec)) * Math.sin(Math.toRadians(ah))) / Math.cos(Math.toRadians(h))));
	}
	
	private Double energieSolaire(int j) {
		// return 1367 * (1 + 0.0334 * cosd(360 * (j - 2.7206) / 365.25));
		return (1367 * (1 + 0.0334 * Math.cos(Math.toRadians(360 * (j - 2.7206) / 365.25))));	
	}
	
	private Double rayonnementSolaireDiffus(int inclinaison, Double hauteur) {
		// CALCUL SCALAIRE
		// return 125 * sind(hauteur)^0.4 * ((1 + cosd(inclinaison)) / 2) + 211.86 * sind(hauteur)^1.22 * ((1 - cosd(inclinaison)) / 2);
		return (125 * Math.pow(Math.sin(Math.toRadians(hauteur)), 0.4) * ((1 + Math.cos(Math.toRadians(inclinaison))) / 2) + 211.86 * Math.pow(Math.sin(Math.toRadians(hauteur)), 1.22) * ((1 - Math.cos(Math.toRadians(inclinaison))) / 2));
	}
	
	private Double rayonnementSolaireDirect(Double temperature, Double hauteur, Double energieSolaire, Double B) {
		// CALCUL SCALAIRE

		// B = 0,02 pour un lieu situé en montagne
		// B = 0,05 pour un lieu rural
		// B = 0,10 pour un lieu urbain
		// B = 0,20 pour un lieu industriel (atmosphère polluée)
		// http://www.emse.fr/~bouchardon/enseignement/processus-naturels/up1/web/wiki/MC%20-%20Atm%20-%20Vapeur%20saturante%20-%20Coup.htm
	
		Double hr = 0.5;
		
		//Pvs = 5.3e-06*temperature^4+ 0.00036*temperature^3+0.00069*temperature^2+ 0.37*temperature+8.4;
		Double pvs = 5.3*Math.pow(10, -6) * Math.pow(temperature,4) + 0.00036 * Math.pow(temperature,3) + 0.00069 * Math.pow(temperature,2) + 0.37 * temperature + 8.4;
		
		Double pv = pvs * hr;
		
		Double tl = 2.4 + 14.6 * B + 0.4 * (1 + 2 * B) * Math.log(pv);
		return (energieSolaire * Math.exp(-tl / (0.9 + 9.4 * Math.sin(Math.toRadians(hauteur)))));
	}
	
	private Double coeficientIncidence(int orientation, int inclinaison, Double azimuth, Double hauteur) {
		// CI = sind(inclinaison) * cosd(hauteur) * cosd(orientation - azimuth) + cosd(inclinaison) * sind(hauteur);
		Double CI = Math.sin(Math.toRadians(inclinaison)) * Math.cos(Math.toRadians(hauteur)) * Math.cos(Math.toRadians(orientation - azimuth)) + Math.cos(Math.toRadians(inclinaison)) * Math.sin(Math.toRadians(hauteur));
		if (CI  < 0) {
			CI = 0.0;
		}
		
		return CI;
	}
	
	private Double eclairementPonctuel(int j, Double latitude, int tSV, int inclinaison, int orientation, Double B, Double temperature) {
		// CALCUL SCALAIRE POUR TOUT CE QUI EST DANS CES FONCTIONS !!!! 
		// AINSI QUE LES PARAMÈTRES!

		// Dec = asind(0.398 * sind(0.985 * j - 80));
		Double dec = Math.toDegrees(Math.asin(0.398 * Math.sin(Math.toRadians(0.985 * j - 80))));
		
		int ah = 180 * (tSV / 12 - 1);
		
		Double hauteur = calculHauteur(latitude, dec, ah);
		Double azimuth = calculAzimuth(dec, ah, hauteur);
		
		Double nrjSolaire = energieSolaire(j);
		
		Double rayonnementDiffus = rayonnementSolaireDiffus(inclinaison, hauteur);
		
		Double rayonnementDirect = rayonnementSolaireDirect(temperature,hauteur, nrjSolaire, B);
		
		Double coefIncidence = coeficientIncidence(orientation, inclinaison,azimuth, hauteur);
		
		Double rayonnementDirectRecu = rayonnementDirect * coefIncidence;
		
		Double eclairement = rayonnementDirectRecu + rayonnementDiffus;
		
		return eclairement;
	}
	
	private Eclairement courbeAnnuelEclairement(int orientationPanneau, Double latitude, Temperature temperatures) {
		int inclinaisonPanneau = 45; 
		Double environnement = 0.1;
		Eclairement eclairements = new Eclairement(N, 0.0);

		for (int j = 0; j < 365; j++) {
			for(int h = 0; h < 24; h++) {
		         Double val = eclairementPonctuel(j+1, latitude, h, inclinaisonPanneau, orientationPanneau, environnement, temperatures.getCourbeTemperatures().getPoint(j * 24 + h));
		         eclairements.getCourbeEclairements().setPoint(j*24+h, val);
			}
		}
		
		return eclairements;
	}
	
	private Courbe conversionPanneauSolaire(Eclairement eclairements, Temperature temperatures, int orientationPanneau, Double surfaceDisponible) {
		// Puissance pour 1000W/m² et température à 25°C
		Double rendementPanneau = _puissancePanneau / 1000;
		Double nbPanneauTotal = Math.floor(surfaceDisponible / _surfacePanneau);
		Double perteEquipement = 0.1;
		
		Courbe courbeEclairements = eclairements.getCourbeEclairements();
		Courbe courbeTemperatures = temperatures.getCourbeTemperatures();
		
		// productiblePanneauSolaire = (rendementPanneau*surfacePanneau*eclairement - coefTemperature*temperature);
		courbeEclairements = courbeEclairements.multiple(rendementPanneau*_surfacePanneau);
		courbeTemperatures = courbeTemperatures.multiple(_coefTemperature);
		
		Courbe productiblePanneauSolaire = courbeEclairements.add(courbeTemperatures.multiple(-1.0));
		
		//  productiblePanneauSolaire= productiblePanneauSolaire - productiblePanneauSolaire*perteEquipement;
		productiblePanneauSolaire = productiblePanneauSolaire.add(productiblePanneauSolaire.multiple(-perteEquipement));
		
		for (int j = 0; j < 365; j++) {
			for (int h = 0; h < 24; h++) {
			       if (productiblePanneauSolaire.getPoint(j * 24 + h) < 0){
				        productiblePanneauSolaire.setPoint(j * 24 + h, 0.0);
			       }
			}
		}
		
		productiblePanneauSolaire = productiblePanneauSolaire.multiple(nbPanneauTotal);
		
		return productiblePanneauSolaire;
	}

	
	public Courbe courbeAnnuelProductionPV(Double latitude ,Temperature temperatures, Double surfaceDisponible) {
		int orientationPanneau = -45;
		
		Eclairement eclairements = courbeAnnuelEclairement(orientationPanneau, latitude, temperatures);
		
		Courbe productible = conversionPanneauSolaire(eclairements, temperatures, orientationPanneau, surfaceDisponible);
		
		productible = productible.multiple((double) (1 / 1000));
		
		return productible;
	}   
}